#include "videog.h"

/** @defgroup Video Video 
 * @{
 *
 * Entering/leaving/utility video functions
 */

/** Enter graphics mode, enabling near pointers and mapping video physical memory
 * to program virtual address.
 *
 * Returns a generic pointer pointing to video memory address or NULL on error. 
 * "mode" specifies the VESA graphics mode to use, and
 * the mapping information is returned through "map".
 *
 * Also initializes two global variables, VRES and HRES,
 */

unsigned long getVBEAddress(unsigned long endereco) {
	__dpmi_regs regs;
	unsigned long dosbuf, address=0;

	dosbuf = __tb & 0xFFFFF; /* dosbuf é a área de transferência */
	dosmemput(&address, 4, dosbuf+40); /* apaga bytes 40 a 43 dessa área */

	regs.x.ax = 0x4F01; /* VBE get mode info */
	regs.x.di = dosbuf & 0xF; /* LSB da área de transferência */
	regs.x.es = (dosbuf >> 4) & 0xFFFF; /*MSBda área de transferência */
	regs.x.cx = 0x0111 & endereco; /* informação sobre o modo */
	__dpmi_int(0x10, &regs); /* DOS int 10h */
	if (regs.h.ah) {
		/* tratamento de erro, modo não suportado */
		printf("erro");
		return 0;
	}

	dosmemget(dosbuf+40, 4, &address); /* lê o endereço da memória vídeo */
	if (!address) {
		/* tratamento de erro, modo não suportado */
		printf("erro");
		return 0;
	}
	return address;
}

int VRES;
int HRES;
char * enter_graphics(int mode, __dpmi_meminfo *map) {
	// Entrar em modo gráfico
	/*	Mode:	640x480 0x101	800x600 0x103
				1024x768 0x105	1280x1024 0x107
	*/
	unsigned long endereco = 0x0000;
	
	switch(mode) {
		case 101: endereco = 0x4101; HRES = 640; VRES = 480; break;
		case 103: endereco = 0x4103; HRES = 800; VRES = 600; break;
		case 105: endereco = 0x4105; HRES = 1024; VRES = 768; break;
		case 107: endereco = 0x4107; HRES = 1280; VRES = 1024; break;
		default: return NULL;
	}

	// Inicio do modo gráfico
	__dpmi_regs regs; /*__dpmi_regs é uma estrutura que representaos registos do CPU */
	regs.x.ax = 0x4F02; /* registo AX do CPU com valor 0x4f02 */
	regs.x.bx = endereco; /* registo BX do CPU com valor 0x4105 */
	__dpmi_int(0x10, &regs); /* gera interrupção de software 0x10, activando o modo gráfico */
	
	
	// Mapeamento gráfico
	char * video_mem = NULL;
	__djgpp_nearptr_enable(); // permite acesso à memória física usando apontadores
	//map->address = 0xC0000000; // endereço físico da memória vídeo
	map->address = getVBEAddress(endereco); // endereco produzido pela funcao
	map->size = HRES*VRES; // tamanho do bloco de memória (matriz)
	__dpmi_physical_address_mapping(map); // video_mem contém o endereço genérico a usar
	video_mem = (char *)(map->address + __djgpp_conventional_base);
	
	return video_mem;
}

/** Unmap video memory, disable near pointer and returns to text mode
 */
void leave_graphics(__dpmi_meminfo *map) {
	// Libertar mapeamento
	__dpmi_free_physical_address_mapping(map);
	__djgpp_nearptr_disable();
	
	// Sair de modo gráfico
	__dpmi_regs regs; // __dpmi_regs é uma estrutura que representaos registos do CPU
	regs.x.ax = 0x0003; // registo AX do CPU com valor 0x03
	__dpmi_int(0x10, &regs); // gera interrup ¸c~ao software 0x10, entrando no modo texto
}

/** Draws a pixel of color "color" at screen coordinates x, y at memory address "base"
 */

/* 
void set_pixel2(int x, int y, int color, char * base) {
	
	//__dpmi_regs regs;
	//regs.h.ah = 0x0C;   // function 0Ch = pixel plot
	//regs.h.al = color;
	//regs.x.cx = x;      // x location, from 0..319
	//regs.x.dx = y;      // y location, from 0..199
	//__dpmi_int(0x10,&regs);
	
	// OU
	if (color < 0 || color > 15) return;
	if (x > HRES) x = HRES;
	if (y > VRES) y = VRES;
	if (x < 0) x = 0;
	if (y < 0) y = 0;
	*(base + y*HRES + x) = (char) color;
}*/

void set_large_pixel(int x, int y, int color, char *base) {
	set_pixel(x-1,y-1,color,base); set_pixel(x,y-1,color,base); set_pixel(x+1,y-1,color,base);
	set_pixel(x-1,y,color,base); set_pixel(x,y,color,base); set_pixel(x+1,y,color,base);
	set_pixel(x-1,y+1,color,base); set_pixel(x,y+1,color,base); set_pixel(x+1,y+1,color,base);
}

/** Returns the pixel color at screen coordinates x, y at memory address "base"
 */
int get_pixel(int x, int y, char *base) {
	/*
	__dpmi_regs regs;
	regs.h.ah = 0x0D;   // function 0Ch = pixel plot
	//regs.h.bh = ?;
	regs.x.cx = x;      // x location, from 0..319
	regs.x.dx = y;      // y location, from 0..199
	__dpmi_int(0x10,&regs);
	return regs.h.al;
	*/
	
	char cor = *(base + y*VRES + x);
	return (int) cor;
}

/** Set graphics memory at memory address "base" to "color".
 */
void clear_screen(char color, char *base) {
	/*unsigned int x, y;
	for(y=0; y<VRES; y++) {
		for(x=0; x<HRES; x++) {
			set_pixel(x,y,color,base);
		}
	}*/
	//swap_buffers(image_buffer_original, image_buffer);
	swap_buffers(image_buffer_original, base);
}

void clear_game_screen(char color, char *base) {
	//swap_game_buffers(image_buffer_original, image_buffer);	
	swap_game_buffers(image_buffer_original, base);	
}

void reset_screen(char color, char *base) {
	unsigned int x, y;
	for(y=0; y<VRES; y++) {
		for(x=0; x<HRES; x++) {
			set_pixel(x,y,color,base);
		}
	}
}

/** Draw a line of color "color" between point (xi,yi) and (xf,yf) at memory address "base"
*/
/*void draw_line(int xi, int yi, int xf, int yf, int color, char *base) {
	// Garantir que desenhamos da esquerda sup para direita inf
	if (xi > xf) { int tmp = xi; xi = xf; xf = tmp;	}
	if (yi > yf) { int tmp = yi; yi = yf; yf = tmp;	}
	
	int delta_x, delta_y;
	delta_x = xf - xi;
	delta_y = yf - yi;
	
	double m = (double) delta_y / (double) delta_x;
	
	double x = 0;
	double y = 0;
	
	if ( delta_x < delta_y ) {
		// Maior deslocamento vertical que horizontal
		// Tem que desenhar na vertical
		for (y = yi; y < yf; y++) {
			x = (1/m) * (y - yi) + xi;
			set_pixel((int)ceil(x), y, color, base);
		}
	} else {
		// Varrimento normal
		for (x = xi; x < xf; x++) {
			y = m * (x - xi) + yi;
			set_pixel(x, (int)ceil(y), color, base);
		}
	}
	

}*/
void draw_line(int x0, int y0, int x1, int y1, int color, char *base) {
        // http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html
        int dx = x1 - x0;
        int dy = y1 - y0;

        set_pixel(x0, y0, color, base);
        //*(base + y0*HRES + x0) = color;
        if (abs(dx) > abs(dy)) {          // slope < 1
                float m = (float) dy / (float) dx;      // compute slope
                float b = y0 - m*x0;
                dx = (dx < 0) ? -1 : 1;
                int yy = 0;
                while (x0 != x1) {
                        x0 += dx;
                        yy = floor(m*x0 + b);
                        set_pixel(x0, yy, color, base);
                        //*(base + yy*HRES + x0) = color;
                }
        } else
        if (dy != 0) {                              // slope >= 1
                float m = (float) dx / (float) dy;      // compute slope
                float b = x0 - m*y0;
                dy = (dy < 0) ? -1 : 1;
                int xx = 0;
                while (y0 != y1) {
                        y0 += dy;
                        xx = floor(m*y0 + b);
                        set_pixel(xx, y0, color, base);
                }
        }
}

void draw_rect(int x1, int y1, int x2, int y2, int color, char *base)
{
	draw_line(x1, y1, x2, y1, color, base);
	draw_line(x2, y1, x2, y2, color, base);
	draw_line(x2, y2, x1, y2, color, base);
	draw_line(x1, y2, x1, y1, color, base);
}


void drawCharAt(char c, int x, int y, int fore_color, int back_color, int char_scale, char *table, char *base) {
	// posição do carácter 'c' em 'table' (lida de po.dat)
	char *char_def = table + c*CHR_H;
	// usar operadores bitwise para isolar e testar os bits
	int j = 0; int m = 0; int k = 0;
	for (j=0; j<CHR_H; j++) { // for each character row
		for (m=0; m<char_scale; m++) {
			long mask = 0x80; // left pixel of current row
			for (k=0; k<8; k++) { // for each pixel in row
				if (*char_def & (mask >> k)) // test pixel k
				//set_pixel(x, y, fore_color, video_mem);
				*(base + y*HRES + x) = fore_color;
			}
		}
	}
}

void swap_buffers(char* buffer, char* base)
{
	memcpy(base, buffer, HRES * VRES * sizeof(char));
}

void swap_game_buffers(char* buffer, char* base)
{
	memcpy(base + TOP_BORDER * HRES, buffer + TOP_BORDER * HRES, HRES * (VRES - TOP_BORDER) * sizeof(char));
}
/** @} end of video */ 
